#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <functional>
#include <pthread.h>
#include "log.hpp"
#include "threadpool.hpp"
#include "InetAddr.hpp"

using func_t=std::function<void(void)>;

enum
{
    socket_error = 1,
    bind_error,
    listen_error,
};

const static int backlog = 5;

class tcpserver;

class ThreadData
{
public:
    ThreadData(int sockfd,struct sockaddr_in addr,tcpserver* s)
        :_sockfd(sockfd)
        ,_addr(addr)
        ,_s(s)
    {}
    int _sockfd;
    InetAddr _addr;
    tcpserver* _s;
};

class tcpserver
{
public:
    tcpserver(uint16_t port) : _listen_sockfd(-1), _port(port)
    {
    }

    void init()
    {
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            Log(FATAL, "socket create false");
            exit(socket_error);
        }
        Log(DEBUG, "socket create success");
        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(_port);
        addr.sin_addr.s_addr = INADDR_ANY;
        int ret = bind(_listen_sockfd, (sockaddr *)&addr, sizeof(addr));
        if (ret < 0)
        {
            Log(FATAL, "bind false:%s",strerror(errno));
            exit(bind_error);
        }
        Log(DEBUG, "bind success");
        // 监听
        ret = listen(_listen_sockfd, backlog);
        if (ret < 0)
        {
            Log(FATAL, "listen false");
            exit(listen_error);
        }
    }

    static void* threadsocktask(void*argc)
    {
        ThreadData*p=(ThreadData*)argc;
        p->_s->server(p->_sockfd,p->_addr);
        delete p;
        return nullptr;
    }

    void start()
    {
        isrunning = true;
        while (isrunning)
        {
            struct sockaddr_in peer;
            socklen_t peerlen=sizeof(peer);
            int sockfd = accept(_listen_sockfd, (struct sockaddr *)&peer, &peerlen);
            if (sockfd < 0)
            {
                Log(WARNING, "accept false:%s",strerror(errno));
                continue;
            }
            //版本一:一次只能处理一个请求,即只能服务一个客户端
            //server(sockfd,InetAddr(peer));

            //版本二:采用多进程,达到服务多个客户端的目的(让子进程去通过继承下来的sockfd服务客户端)
            // pid_t id=fork();
            // if(id>0)
            // {
            //     //child
            //     close(_listen_sockfd);
            //     if(fork()>0)exit(0);//子进程直接退出，让孙子进程来服务客户端，这样孙子进程结束任务后编程僵尸进程直接被回收，就不用父进程去等待了
            //     server(sockfd,InetAddr(peer));
            //     exit(0);//孙子进程执行完任务后直接退出
            // }
            // //此时父进程已经不需要直接服务客户端了
            // close(sockfd);

            //版本三:多线程
            // pthread_t id;
            // ThreadData*data=new ThreadData(sockfd,peer,this);
            // pthread_create(&id,nullptr,&threadsocktask,data);
            // pthread_detach(id);

            //版本四:线程池
            func_t task=std::bind(&tcpserver::server,this,sockfd,InetAddr(peer));
            ThreadPool<func_t>::GetInstance()->push(task);
        }
        isrunning = false;
    }

    void server(int sockfd,InetAddr addr)
    {
        Log(DEBUG,"get a new link:%s %d",addr.ip().c_str(),addr.port());
        std::string clientaddr="["+addr.ip()+":"+std::to_string(addr.port())+"]#";
        while(true)
        {
            char buffer[1024];
            //tcp面向字节流，不能保证buffer是一个完整的字符串
            //对于应用层面，必须保证收到的是一个完整的请求
            int n=read(sockfd,buffer,sizeof(buffer));
            if(n>0)
            {
                buffer[n]='\0';
                std::cout<<clientaddr<<buffer<<std::endl;
                //返回数据
                std::string echo_message("[server echo]#");
                echo_message+=buffer;
                write(sockfd,echo_message.c_str(),sizeof(echo_message));
            }
            else if(n==0)
            {
                break;
            }
            else
            {
                Log(ERROR,"read false:%s",strerror(errno));
                break;
            }
        }
        close(sockfd);
    }

private:
    int _listen_sockfd;
    uint16_t _port;
    bool isrunning;
};
